feat(release): reconcile the attested SBOM against the staged install - #310
Merged
Conversation
verify-sbom-scope gains an opt-in --reconcile <stagingDir> that reads the
frozen prod staging tree's real package manifests (never the pnpm dir-name
encoding) and requires the attested SBOM's {name@version} set to match,
catching a syft parsing regression or an altered SBOM that carries
syntactically-valid-but-wrong versions past the syntax-only checks.
The flag is off by default (live-release path byte-unchanged); publish.yml
runs it as a NON-gating shadow step to collect pinned-syft-vs-derivation
evidence on real staging trees before it can be promoted to a gate. The
staging self-package is excluded by exact name@version, so a forged self
entry is still flagged. Fail-closed throughout (exit 2 on any staging read
failure or malformed manifest; empty inventory can only fail, never pass).
reconcileSbomInventory filters SBOM packages through npmPackages() so a no-purl SPDX document-root or a non-npm (pypi) entry is not misclassified as 'unexpected'. No reconcile-side test pinned that filter — dropping it kept every test green. Add a reconcile case feeding a no-purl root + a pypi entry and asserting a clean reconcile, so removing the discriminator fails.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
scripts/verify-sbom-scope.mjspreviously checked only version syntax (exact semver), so a syft parsing regression or an altered SBOM carrying syntactically-valid-but-wrong versions (e.g.@codemirror/state@0.0.0) passed the release gate. This adds an opt-in--reconcile <stagingDir>that reconciles the attested SBOM's{name@version}set against an independent inventory read from the frozen prod staging tree's real package manifests (not the pnpm dir-name encoding — that is not a stable contract).The strict check is off by default (the live-release path is byte-unchanged) and reaches CI only as a non-gating shadow step in
publish.yml, so it collects pinned-syft (v1.42.3) vs manifest-derivation evidence on real staging trees before it can be promoted to a gate. That promotion is a tracked follow-up.Changes
scripts/verify-sbom-scope.mjs:deriveInstalledInventory(stagingDir)— fs adapter reading each installed package's realpackage.jsonfromnode_modules/.pnpm/*/node_modules/(symlinked deps skipped; encoding-independent). Fail-closed: throws on unreadable.pnpm, a.pnpmentry with no manifest, or a manifest missingname/version.reconcileSbomInventory({ sbom, installed, ignore })— pure, set-based per-name version comparison (surplus→unexpected, deficit→missing, fully-disjoint→version-mismatch).ignoreexcludes the staging self-package by exactname@version, so a forged self entry at any other version is still flagged.resolveReconcileArg(argv)— pure flag parser; missing value → exit 2.main()wires the opt-in flag: syntax gate first, then reconcile; staging read failure → exit 2, violation → exit 1..github/workflows/publish.yml: non-gating (|| true) shadow reconcile step after the existing gating step; gating step untouched.Related
Test Plan
pnpm exec vitest run test/build/verify-sbom-scope.test.ts— 49 passpnpm compile+pnpm lintgreen on changed files